package ca.etsmtl.log120.tp1.system;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Observable;
import java.util.Observer;

import ca.etsmtl.log.util.IDLogger;
import ca.etsmtl.log120.tp1.io.SocketCom;
import ca.etsmtl.log120.tp1.lists.ShapeLinkedList;
import ca.etsmtl.log120.tp1.shapehierarchy.AbstractShape;
import ca.etsmtl.log120.tp1.shapehierarchy.Circle;
import ca.etsmtl.log120.tp1.shapehierarchy.Line;
import ca.etsmtl.log120.tp1.shapehierarchy.Oval;
import ca.etsmtl.log120.tp1.shapehierarchy.Rectangle;
import ca.etsmtl.log120.tp1.shapehierarchy.Square;

/******************************************************
Cours : LOG120
Session : E2011
Groupe : 01
Projet : Laboratoire #2
Etudiants : Nicolas Blais Miko
 	 		Yohan Trepanier-Montpetit
Codes perm. : BLAN24079109
			  TREY16079108
Professeur : Chamseddine Talhi
Charge de labo : Eric Goulet
Nom du fichier : ShapeCreator.java
Date cree : 2011-05-23
Date dern. modif. 2015-05-23
 *************************************************** ****
Historique des modifications
 *************************************************** ****
2011-05-23 Version initiale (Yohan Trepanier)
 ***************************************************/

/**
 * Classe permettant de creer des formes, de les ajouter a la liste et de les logger dans le IDLogger
 */
public class ShapeCreator implements Observer{

	//Les types possibles
	private static final String[] SHAPE_TYPES = {"CERCLE", "OVALE", "RECTANGLE", "CARRE", "LIGNE"};

	//Les commande de communication avec le serveur
	private static final String GET_COMMAND = "GET";

	//Concernant la liste de formes
	private static final int NB_FORMES = 10;
	public ShapeLinkedList shapeList = new ShapeLinkedList(NB_FORMES);
	private ShapeLinkedList memoryList = new ShapeLinkedList(NB_FORMES);

	//L'objet pour la communication poar socket
	SocketCom socketCom = new SocketCom();

	//Pour decoder les String
	Decoder decoder;

	//Le IDLogger pour maintenir un historique des numeros de sequences
	IDLogger logger = IDLogger.getInstance();

	/**
	 * Constructeur par defaut
	 */
	public ShapeCreator(){
		//on s'abonne au socket
		socketCom.addObserver(this);
	}

	/**
	 * Connection a un serveur a travers le socket
	 * @param address Le IP du serveur
	 * @param port Le port du serveur
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	public void connectToServer(String address, int port) throws UnknownHostException, IOException{
		socketCom.connect(address, port);
	}

	public void closeConnection(){
		socketCom.close();
	}

	/**
	 * Methode qui permet de verifier si la connection avec le serveur est fermee
	 * @return Vrai si la connection avec le serveur est fermee
	 */
	public boolean connectionClosed(){

		return socketCom.isClosed();
	}

	/**
	 * Demande d'une forme au serveur
	 */
	public void requestShape(){
		socketCom.send(GET_COMMAND);
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		//Cree une forme lorsque le socket recoit de l'info
		//on ajoute aussi cette forme a la liste de formes et a une liste memorisant les positions initiales
		AbstractShape newShape = createShape(socketCom.getMessage());
		shapeList.add(newShape);
		memoryList.add(newShape.copyOf());
	}

	/**
	 * Crée une nouvelle forme. Cette méthode reçoit la chaîne de
	 * caractères provenant du serveur de formes, elle détermine de quelle
	 * forme il s'agit et applique l'opérateur new sur le constructeur de
	 * la forme désirée. Elle log aussi le numero de sequence dans le IDLogger
	 *
	 * @param shapeStringt un objet String contenant la chaîne de caractères
	 *                    qui décrit une forme et provenant du serveur de
	 *                    formes.
	 *
	 * @return une instance d'une des sous-classes de la classe abstraite
	 *         Forme avec les paramètres passés par la chaîne d'entrée.
	 */
	private AbstractShape createShape(String shapeString) {

		//		System.out.println("ShapeCreator: Creating shape");

		AbstractShape shape = null;

		//Decodage du message avec le string du socket
		decoder = new Decoder(shapeString);

		//Demande des proprietes et du type de la forme
		String type = decoder.getType();
		int[] prop = decoder.getProperties();

		int typeNumber = -1; //Pour le type, en chiffre

		//On trouve la forme est de quel type
		for(int i =0; i<SHAPE_TYPES.length; i++){
			if(type.equals(SHAPE_TYPES[i]))
				typeNumber = i;
		}

		//dependant du type, on cree un objet different
		switch(typeNumber){
		case 0:
			shape = new Circle(prop[0], prop[1], prop[2], prop[3]);
			break;
		case 1:
			shape = new Oval(prop[0], prop[1], prop[2], prop[3], prop[4]);
			break;
		case 2:
			shape = new Rectangle(prop[0], prop[1], prop[2], prop[3], prop[4]);
			break;
		case 3:
			shape = new Square(prop[0], prop[1], prop[2], prop[3], prop[4]);
			break;
		case 4:
			shape = new Line(prop[0], prop[1], prop[2], prop[3], prop[4]);
			break;


		}

		//On garde en m�moire le numero de sequence de la forme que l'on cree
		logger.logID(shape.getNseq());

		//On retourne la forme cree
		return shape;
	}

	/**
	 * Methode permettant de vider la liste de forme pour vider l'affichage
	 */
	public void clearAffichage(){

		shapeList.clear();
		memoryList.clear();
	}

	/**
	 * Methode qui s'occupe du trie de la liste de formes
	 * @param sortType Le type de trie a utiliser
	 * @param upSelected Si on trie la liste en ordre croissant ou decroissant
	 */
	public void sort(int sortType, boolean upSelected) {

		if(upSelected) // Si on veut trier en ordre croissant
			switch(sortType){ // On trie en ordre croissant selon le trie specifie
			case 0:
				shapeList.sortAreaUp();
				break;
			case 1:
				shapeList.sortSeqUp();
				break;
			case 2:
				shapeList.sortShapeUp();
				break;
			case 3:
				shapeList.sortDistUp();
				break;
			case 4:
				shapeList.sortHeightUp();
				break;
			case 5:
				shapeList.sortWidthUp();
				break;
			}
		else{ // Sinon, ordre decroissant
			switch(sortType){ // On trie en ordre decroissant selon le trie specifie
			case 0:
				shapeList.sortAreaDown();
				break;
			case 1:
				shapeList.sortSeqDown();
				break;
			case 2:
				shapeList.sortShapeDown();
				break;
			case 3:
				shapeList.sortDistDown();
				break;
			case 4:
				shapeList.sortHeightDown();
				break;
			case 5:
				shapeList.sortWidthDown();
				break;
			}

		}

		if(sortType == 6){ //Si on veut reprendre l'ordre de depart
				// On remet les formes initiales dans la liste
				shapeList.orderReset((AbstractShape[])memoryList.toArray());
		}else{ //sinon
			// On ajuste les positions des formes sur l'ecran selon leur ordre dans la liste 
			shapeList.updatePositions();
		}

	}
}
